package handlers

import (
	"encoding/json"
	"fmt"
	"gitee.com/Cookie_XiaoD/go-filestore-server/common"
	"gitee.com/Cookie_XiaoD/go-filestore-server/config"
	"gitee.com/Cookie_XiaoD/go-filestore-server/db"
	"gitee.com/Cookie_XiaoD/go-filestore-server/db/entities"
	"gitee.com/Cookie_XiaoD/go-filestore-server/meta"
	"gitee.com/Cookie_XiaoD/go-filestore-server/mq"
	"gitee.com/Cookie_XiaoD/go-filestore-server/store/alioss"
	"gitee.com/Cookie_XiaoD/go-filestore-server/util"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"strconv"
	"strings"
	"syscall"
	"time"
)

//UploadHandler 上传文件
func UploadHandler(w http.ResponseWriter, r *http.Request) {
	if r.Method == "GET" {
		SendStaticHTML("../../static/view/index.html", w, r)
		return
	}
	//处理文件流
	if r.Method == "POST" {
		//获取上传文件信息，存储到本地目录
		mFile, fileHeader, err := r.FormFile("upFile")
		if err != nil {
			log.Printf("获取上传文件失败:%v", err.Error())
			SendJsonResp(w, BuildArgsErrResp())
			return
		}
		defer mFile.Close()
		log.Printf("上传的文件名：%v", fileHeader.Filename)

		//创建文件元信息
		fileMeta := &meta.FileMeta{
			FileName: fileHeader.Filename,
			Location: "../../UploadFiles/" + fileHeader.Filename,
			UploadAt: time.Now().Format("2006-01-02 15:04:05"),
		}
		//如果目录不存在则创建
		if err := os.MkdirAll("../../UploadFiles", 0777); err != nil {
			log.Printf("创建本地文件夹失败:%v", err.Error())
			SendServerErrResp(w)
			return
		}

		//if err:=syscall.Chmod("../../UploadFiles",0777);err!=nil{
		//	log.Printf("修改文件夹权限失败:%v", err.Error())
		//	io.WriteString(w, "内部错误")
		//	return
		//}
		//本地文件，不存在则创建，存在则覆盖
		//TODO:暂时不明白，为什么第一种方式得到的文件计算哈希结果不对
		//localFile, err := os.OpenFile(fileMeta.Location, os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0666)
		localFile, err := os.Create(fileMeta.Location)
		if err != nil {
			log.Printf("创建本地文件失败:%v", err.Error())
			SendServerErrResp(w)
			return
		}
		defer localFile.Close()

		//将上传的文件拷贝到本地文件中
		if fileMeta.FileSize, err = io.Copy(localFile, mFile); err != nil {
			log.Printf("拷贝上传文件到本地失败:%v", err.Error())
			SendServerErrResp(w)
			return
		}
		//设置文件的下一次读写位置，为从头的第一个字节开始,否者localfile当前位置处于末尾
		//等同与		localFile.Seek(-fileMeta.FileSize, io.SeekEnd)
		localFile.Seek(0, io.SeekStart)
		fileMeta.FileHash = util.FileSha1(localFile)
		//meta.UpdateFileMeta(fileMeta)

		//TODO:同时存储文件到OSS
		localFile.Seek(0, io.SeekStart)
		ossPath := "oss/" + fileMeta.FileHash

		transData := mq.TransferData{
			FileHash:      fileMeta.FileHash,
			DestStoreType: common.OSS,
			CurLocation:   fileMeta.Location,
			DestLocation:  ossPath,
		}
		transBytes, err := json.Marshal(transData)
		if err != nil {
			SendJsonResp(w, BuildServerErrResp())
			return
		}

		if pushRes := mq.PushMessage(config.RabbitMQCfg.TransExchangeName, config.RabbitMQCfg.TransOSSRoutingKey, transBytes); !pushRes {
			SendJsonResp(w, BuildServerErrResp())
			return
		}

		//err=alioss.GetBucket().PutObject(ossPath,localFile)
		//
		//if err!=nil{
		//	log.Printf("上传文件到阿里云OSS失败:%v", err.Error())
		//	//SendServerErrResp(w)
		//	//return
		//}else{
		//	//上传oss成功，替换文件路径，此时的文件路径其实是对象在oss中的key，下载时，利用该key获取临时签名的文件OSS下载地址
		//	fileMeta.Location=ossPath
		//	log.Println("上传文件到阿里云OSS成功")
		//}

		result := meta.UpdateFileMetaDB(fileMeta)
		if !result {
			SendJsonResp(w, BuildServerErrResp())
			return
		}

		log.Printf("文件hash:%v", fileMeta.FileHash)

		//文件上传完，且存库成功后，保存用户文件信息
		//若此步骤失败，则重传即可
		//不采用失败，因此若把保存用户文件信息移到保存文件信息之前，则可能保存文件失败但用户文件表却有了相关信息

		r.ParseForm()
		log.Println("准备获取用户名")
		userName := r.Form.Get("username")
		log.Println("准备获取用户名" + userName)
		result = db.AddUserFileInfo(&entities.AddUserFileEntity{
			UserName: userName,
			FileName: fileMeta.FileName,
			FileSize: fileMeta.FileSize,
			FileHash: fileMeta.FileHash,
		})
		if !result {
			log.Printf("保存用户文件信息失败")
			SendJsonResp(w, BuildServerErrResp())
			return
		}

		//log.Println("上传文件成功，进行重定向")
		//上传成功，重定向到成功页面
		//http.Redirect(w, r, "/file/upload/success", http.StatusFound)
		data := struct {
			FileInfo    *meta.FileMeta `json:"fileInfo"`
			RedirectUrl string         `json:"redirectUrl"`
		}{
			FileInfo:    fileMeta,
			RedirectUrl: "/views/home.html",
		}
		SendJsonResp(w, BuildSuccessResp(data))
	}

}

//UploadSuccessHandler 上传文件成功
func UploadSuccessHandler(w http.ResponseWriter, r *http.Request) {
	resp := BuildSuccessResp(nil)
	SendJsonResp(w, resp)
}

//GetFileMetaHandler 获取文件元信息
func GetFileMetaHandler(w http.ResponseWriter, r *http.Request) {
	r.ParseForm()
	fh := r.Form.Get("filehash")
	if fh == "" {
		SendJsonResp(w, BuildArgsErrResp())
		return
	}
	log.Println("获取文件元信息" + fh)
	//fileMeta := meta.GetFileMeta(fh)
	fileMeta, err := meta.GetFileMetaDB(fh)
	if err != nil {
		if err == util.NoResultError {
			SendJsonResp(w, BuildFailResp(FileNotFoundCode, FileNotFoundMsg))
			return
		}
		SendServerErrResp(w)
		return
	}

	//if fileMeta == nil {
	//	SendServerErrResp(w)
	//	return
	//}
	log.Println("获取文件元信息" + fileMeta.FileHash)
	SendJsonResp(w, BuildSuccessResp(fileMeta))
}

//DownloadFileHandler 下载文件
func DownloadFileHandler(w http.ResponseWriter, r *http.Request) {
	r.ParseForm()
	fh := r.Form.Get("filehash")
	if fh == "" {
		SendJsonResp(w, BuildArgsErrResp())
		return
	}
	//根据文件哈希获取文件元信息,再读取文件
	fileMeta, err := meta.GetFileMetaDB(fh)
	if err != nil {
		if err == util.NoResultError {
			SendJsonResp(w, BuildFailResp(FileNotFoundCode, FileNotFoundMsg))
			return
		}
		SendServerErrResp(w)
		return
	}
	//if fileMeta == nil {
	//	SendJsonResp(w, BuildFailResp(FileNotFoundCode, FileNotFoundMsg))
	//	return
	//}

	file, err := os.Open(fileMeta.Location)
	if err != nil {
		if err == syscall.ERROR_FILE_NOT_FOUND {
			SendJsonResp(w, BuildFailResp(FileNotFoundCode, FileNotFoundMsg))
		} else {
			SendServerErrResp(w)
		}
		return
	}
	defer file.Close()
	fileBytes, err := ioutil.ReadAll(file)
	if err != nil {
		SendServerErrResp(w)
		return
	}
	//应答为二进制流，不知道什么类型
	w.Header().Set("content-type", "application/octet-stream")
	//应答作为附件下载，默认文件名为 实际文件名
	w.Header().Set("Content-Disposition", "attachment;filename="+fileMeta.FileName)
	w.Write(fileBytes)

}

//UpdateFileMetaHandler 更新文件元信息
func UpdateFileMetaHandler(w http.ResponseWriter, r *http.Request) {
	if r.Method != "POST" {
		w.WriteHeader(http.StatusBadRequest)
		return
	}

	bodyBytes, err := ioutil.ReadAll(r.Body)
	if err != nil {
		SendJsonResp(w, BuildArgsErrResp())
		return
	}

	req := &updateFileMetaReq{}
	if err := json.Unmarshal(bodyBytes, req); err != nil {
		SendServerErrResp(w)
		return
	}

	//TODO：目前仅支持更新文件名操作
	if req.Op != 0 {
		SendJsonResp(w, BuildArgsErrResp())
		return
	}

	if req.Op == 0 && req.FileName == "" {
		SendJsonResp(w, BuildArgsErrResp())
		return
	}

	//获取元信息
	fileMeta := meta.GetFileMeta(req.FileHash)
	if fileMeta == nil {
		SendJsonResp(w, BuildFailResp(FileNotFoundCode, FileNotFoundMsg))
		return
	}
	fileMeta.FileName = req.FileName
	SendJsonResp(w, BuildSuccessResp(fileMeta))
}

//DeleteFileHandler 删除文件元信息与文件
func DeleteFileHandler(w http.ResponseWriter, r *http.Request) {
	r.ParseForm()
	fh := r.Form.Get("fileHash")
	if fh == "" {
		SendJsonResp(w, BuildArgsErrResp())
		return
	}
	fileMeta := meta.GetFileMeta(fh)
	if fileMeta == nil {
		SendJsonResp(w, BuildSuccessResp(nil))
		return
	}

	err := os.Remove(fileMeta.Location)
	if err == syscall.ERROR_FILE_NOT_FOUND {
		SendJsonResp(w, BuildSuccessResp(nil))
		return
	}

	if err != nil {
		SendServerErrResp(w)
		return
	}

	meta.DeleteFileMeta(fh)
	SendJsonResp(w, BuildSuccessResp(nil))

}

//QueryFileMetasByUserName 根据用户名查询用户上传的文件元信息
func QueryFileMetasByUserNameHandler(w http.ResponseWriter, r *http.Request) {

	r.ParseForm()
	username := r.Form.Get("username")
	limitStr := r.Form.Get("limit")
	limit, err := strconv.Atoi(limitStr)
	if err != nil {
		SendJsonResp(w, BuildArgsErrResp())
		return
	}
	result, err := db.GetUserFileByUserName(username, limit)
	if err != nil {
		SendServerErrResp(w)
		return
	}
	SendJsonResp(w, BuildSuccessResp(result))
}

//TryFastUploadHandler 秒传
func TryFastUploadHandler(w http.ResponseWriter, r *http.Request) {
	if !checkHttpMethod("POST", r, w) {
		return
	}
	log.Println("开始秒传")
	//bodyBytes,err:=ioutil.ReadAll(r.Body)
	//if err!=nil{
	//	log.Printf("获取Body错误：%v",err.Error())
	//	SendJsonResp(w,BuildArgsErrResp())
	//	return
	//}

	req := &fastUploadReq{}
	err := json.NewDecoder(r.Body).Decode(req)
	//err=json.Unmarshal(bodyBytes,req)
	if err != nil {
		log.Printf("序列化body错误：%v", err.Error())
		SendServerErrResp(w)
		return
	}

	//1.根据文件hash查询文件元信息
	//2.若无法找到元信息，则秒传失败
	_, err = meta.GetFileMetaDB(req.FileHash)
	if err != nil {
		if err == util.NoResultError {
			SendJsonResp(w, BuildFailResp(FileNotFoundCode, FileNotFoundMsg))
			return
		}
		SendServerErrResp(w)
		return
	}

	//3.若能找到，则更新用户信息表，完成秒传
	r.ParseForm()
	userName := r.Form.Get("username")
	result := db.AddUserFileInfo(&entities.AddUserFileEntity{
		UserName: userName,
		FileHash: req.FileHash,
		FileName: req.FileName,
		FileSize: req.FileSize,
	})
	if !result {
		SendServerErrResp(w)
		return
	}
	log.Println("秒传成功")
	SendJsonResp(w, BuildSuccessResp(nil))

}

//GetFileDownloadURL 获取文件的下载地址
func GetFileDownloadURL(w http.ResponseWriter, r *http.Request) {
	r.ParseForm()
	fileHash := r.Form.Get("fileHash")
	if fileHash == "" {
		//SendJsonResp(w,BuildArgsErrResp())
		log.Println("文件哈希为空")
		w.Write([]byte(""))
		return
	}
	//根据文件哈希获取文件元信息，利用文件地址获取实际的下载地址
	fileMeta, err := meta.GetFileMetaDB(fileHash)
	if err != nil {
		log.Println("无法找到文件")
		//SendJsonResp(w,BuildFailResp(FileNotFoundCode,FileNotFoundMsg))
		w.Write([]byte(""))
		return
	}
	filePath := fileMeta.Location
	if filePath == "" {
		log.Println("文件路径为空")
		w.Write([]byte(""))
		return
	}
	//判断文件路径是本地还是阿里云OSS
	if strings.HasPrefix(filePath, "../../UploadFiles") { //本地文件
		token := r.Form.Get("token")
		tmpUrl := fmt.Sprintf("http://%s/file/download?filehash=%s&token=%s",
			r.Host, fileMeta.FileHash, token)
		log.Println("本地文件：" + tmpUrl)
		w.Write([]byte(tmpUrl))
		return
	}

	//阿里云oss路径
	if strings.HasPrefix(filePath, "oss/") {
		w.Write([]byte(alioss.GetSignedURL(filePath)))
	}

}
